package ain.impr;

import java.util.ArrayList;

import javax.swing.text.ZoneView;

import ain.Antibody;

import util.Vector;

import environment.Ball;
import environment.Pitch;
import environment.Team;
import environment.player.Player;

public abstract class ImprAbstractAntibody implements Antibody
{
	// fields used for the ball-robot affinity
	private double[] pitchZoneB; // special case for pitch zones of the top and bottom row
	private double[][] pitchZoneHome; // special case for the pitch zone in front of HOME goal
	private double[][] pitchZoneAway; // special case for the pitch zine in front of AWAY goal
	private double[][] pitchZones; // deprecated
	private double zoneHeight, zoneWidth; // height and width of the predefined pitch zones
	
	protected Pitch pitch;
	protected Player player;
	protected double antigenAffinity;
	
	protected double concentrationChangeValue, concentration;
	
	protected static double FAR_RADIUS = 0;
	protected static double NEAR_RADIUS = FAR_RADIUS / 2;
	
	public ImprAbstractAntibody(Player player, Pitch pitch)
	{
		this.pitch = pitch;
		this.player = player;
		preparePitchZones(player.getTeam());
		
		FAR_RADIUS = pitch.getWidth()/4;
	}
	
	public void antigenAffinity()
	{
		//reset old affinity value with antigens
		antigenAffinity = 0;
		
		//calculate affinity with antigens
		double distBallGoal = affinityBallGoal();
		double distBallPlayer = affinityBallPlayer();
		double environmentState = environmentState();
				
		antigenAffinity = (distBallGoal + distBallPlayer + environmentState()) / 3;
//		antigenAffinity = distBallGoal;
//		System.out.println(toString() + " " + antigenAffinity);
	}
	
	private double environmentState()
	{
		// find minimal distance to ball of team mates
		// put in relation with self distance

		// find minimal distance to ball of opponents
		// put in relation to self distance
		
		double minDistTeamMates = Double.MAX_VALUE;
		double minDistOpponents = Double.MAX_VALUE;
		for (Player p : pitch.getPlayers())
		{
			double distance = p.getPosition().euclDistance(pitch.getBall().getPosition());
			
			if (p.getTeam().equals(player.getTeam()) && p.getID() != player.getID())
			{
				if (distance < minDistTeamMates) minDistTeamMates = distance;
			}
			else if (!p.getTeam().equals(player.getTeam()))
			{
				if (distance < minDistOpponents) minDistOpponents = distance;
			}
		}
		
		double teamRatio = player.getPosition().euclDistance(pitch.getBall().getPosition()) / minDistTeamMates;
		double opntRatio = player.getPosition().euclDistance(pitch.getBall().getPosition()) / minDistOpponents;
		
		
		return (teamDistBallRatio(teamRatio) + opntDistBallRatio(opntRatio)) / 2.0;
	}
	
	abstract protected double teamDistBallRatio(double ratio);
	abstract protected double opntDistBallRatio(double ratio);
	
	private double affinityBallGoal()
	{
		double zoneNumber = 0;
		
		double x = pitch.getBall().getPosition().getX();
		double y = pitch.getBall().getPosition().getY();
		
		// check if special cases apply:
		// A: ball within 1 zoneWidth of either goal and pitch base line
		// B: ball within 1 zoneHeight from long side line
		
		// case A
		if (x <= zoneWidth) // closer to AWAY goal
		{
			int row = (int) (y / zoneHeight);
			zoneNumber = player.getTeam().equals(Team.HOME) ? pitchZoneAway[row][0] : pitchZoneHome[row][0];
			System.out.println(zoneNumber);
			return imprBallGoalValue(zoneNumber);
		}
		if (x >= pitch.getWidth() - zoneWidth) // closer to HOME goal
		{
			int row = (int) (y / zoneHeight);
			zoneNumber = player.getTeam().equals(Team.HOME) ? pitchZoneHome[row][0] : pitchZoneAway[row][0];
			System.out.println(zoneNumber);
			return imprBallGoalValue(zoneNumber);
		}
		
		// zoneNumber = constant * x + yOffset
		double xValue = (player.getTeam().equals(Team.HOME)) ? (x - zoneWidth) / (zoneWidth) :
			(pitch.getWidth() - x - zoneWidth) / (zoneWidth);
		
		//case B
		if (y <= zoneHeight || y >= pitch.getHeight() - zoneHeight)
		{
			// zoneNumber = -(3/4) * xValue + 4
			zoneNumber = -(3.0/4.0) * xValue + 4;
			System.out.println(xValue + " " + zoneNumber);
			return imprBallGoalValue(zoneNumber);
		}
		
		// inner pitch rectangle
		// zoneNumber = -1 * xValue + 5
		zoneNumber = -1 * xValue + 5;
		System.out.println(xValue + " " + zoneNumber);
		return imprBallGoalValue(zoneNumber);
	}
	
	abstract protected double imprBallGoalValue(double zoneNumber);
	
	private double affinityBallPlayer()
	{
		double distance = pitch.getBall().getPosition().euclDistance(player.getPosition());
		
		return distBallPlayerValue(distance);
	}
	
	abstract protected double distBallPlayerValue(double distance);
	
	private void preparePitchZones(Team team)
	{
//		// special case A and A*		
//		if (team.equals(Team.HOME))
//		{
//			double[][] zones = {	{3, 3, 2, 2, 1, 1},
//						{5, 4, 3, 2, 1, 1},
//						{5, 4, 3, 2, 1, 1},
//						{3, 3, 2, 2, 1, 1}};
//			pitchZoneB =  zones[0];
//		}
//		else
//		{
//			double[][] zones = {	{1, 1, 2, 2, 3, 3},
//						{1, 1, 2, 3, 4, 5},
//						{1, 1, 2, 3, 4, 5},
//						{1, 1, 2, 2, 3, 3}};
//			
//			pitchZoneB = zones[0];
//		}
		
		double[][] awayZone = {	{4}, {5}, {5}, {4}};
		pitchZoneAway = awayZone;
		
		double[][] homeZone = {	{1}, {1}, {1}, {1}};
		pitchZoneHome = homeZone;
		
		// calculate the height and width of a zone. this is needed for the ball-robot affinity
		zoneHeight = pitch.getHeight() / 4;
		zoneWidth = pitch.getWidth() / 6;
	}
	
	public void concentrationChange()
	{
		concentration = 1 / (1 + Math.exp(0.5 - concentrationChangeValue));
		if (concentration < 0)
		{
			System.out.println("WARNING - AA: concentration negative: " + toString() + " " + concentration);
			concentration = 0;
		}
		concentrationChangeValue = 0;
	}
	
	public void setConcentrationChange(double value)
	{
		concentrationChangeValue = value;
	}
	
	public double getConcentration()
	{		
		return concentration;
	}
	
	public double getAntigenAffinity()
	{
		return antigenAffinity;
	}
}
